"""
指标收集器 - 提供应用性能指标收集、存储和导出功能
支持Prometheus格式、内存存储、自定义收集器等
"""

import time
import threading
import asyncio
import functools
from typing import (
    Dict, List, Optional, Any, Callable, Union, Protocol,
    runtime_checkable, TypeVar, Awaitable
)
from dataclasses import dataclass, field
from enum import Enum
from abc import ABC, abstractmethod
from collections import defaultdict, deque
from datetime import datetime, timedelta

from ..logging.manager import get_logger

# 类型变量
F = TypeVar('F', bound=Callable[..., Any])
AsyncF = TypeVar('AsyncF', bound=Callable[..., Awaitable[Any]])

class MetricType(Enum):
    """指标类型"""
    COUNTER = "counter"      # 计数器 - 只能增加
    GAUGE = "gauge"          # 仪表盘 - 可增可减
    HISTOGRAM = "histogram"  # 直方图 - 分布统计
    SUMMARY = "summary"      # 摘要 - 分位数统计

@dataclass
class Metric:
    """指标数据"""
    name: str
    metric_type: MetricType
    value: Union[int, float]
    labels: Dict[str, str] = field(default_factory=dict)
    timestamp: Optional[datetime] = None
    help_text: Optional[str] = None

@dataclass
class Counter:
    """计数器指标"""
    name: str
    value: float = 0.0
    labels: Dict[str, str] = field(default_factory=dict)
    help_text: Optional[str] = None
    
    def inc(self, amount: float = 1.0):
        """增加计数"""
        self.value += amount
    
    def to_metric(self) -> Metric:
        """转换为指标"""
        return Metric(
            name=self.name,
            metric_type=MetricType.COUNTER,
            value=self.value,
            labels=self.labels,
            timestamp=datetime.utcnow(),
            help_text=self.help_text
        )

@dataclass
class Gauge:
    """仪表盘指标"""
    name: str
    value: float = 0.0
    labels: Dict[str, str] = field(default_factory=dict)
    help_text: Optional[str] = None
    
    def set(self, value: float):
        """设置值"""
        self.value = value
    
    def inc(self, amount: float = 1.0):
        """增加值"""
        self.value += amount
    
    def dec(self, amount: float = 1.0):
        """减少值"""
        self.value -= amount
    
    def to_metric(self) -> Metric:
        """转换为指标"""
        return Metric(
            name=self.name,
            metric_type=MetricType.GAUGE,
            value=self.value,
            labels=self.labels,
            timestamp=datetime.utcnow(),
            help_text=self.help_text
        )

@dataclass
class HistogramBucket:
    """直方图桶"""
    le: float  # 小于等于
    count: int = 0

@dataclass
class Histogram:
    """直方图指标"""
    name: str
    buckets: List[HistogramBucket] = field(default_factory=list)
    count: int = 0
    sum: float = 0.0
    labels: Dict[str, str] = field(default_factory=dict)
    help_text: Optional[str] = None
    
    def __post_init__(self):
        if not self.buckets:
            # 默认桶边界
            default_buckets = [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0, float('inf')]
            self.buckets = [HistogramBucket(le=le) for le in default_buckets]
    
    def observe(self, value: float):
        """观察值"""
        self.count += 1
        self.sum += value
        
        for bucket in self.buckets:
            if value <= bucket.le:
                bucket.count += 1
    
    def to_metrics(self) -> List[Metric]:
        """转换为指标列表"""
        metrics = []
        
        # 桶指标
        for bucket in self.buckets:
            bucket_labels = self.labels.copy()
            bucket_labels['le'] = str(bucket.le)
            metrics.append(Metric(
                name=f"{self.name}_bucket",
                metric_type=MetricType.HISTOGRAM,
                value=bucket.count,
                labels=bucket_labels,
                timestamp=datetime.utcnow(),
                help_text=self.help_text
            ))
        
        # 总数指标
        metrics.append(Metric(
            name=f"{self.name}_count",
            metric_type=MetricType.HISTOGRAM,
            value=self.count,
            labels=self.labels,
            timestamp=datetime.utcnow(),
            help_text=self.help_text
        ))
        
        # 总和指标
        metrics.append(Metric(
            name=f"{self.name}_sum",
            metric_type=MetricType.HISTOGRAM,
            value=self.sum,
            labels=self.labels,
            timestamp=datetime.utcnow(),
            help_text=self.help_text
        ))
        
        return metrics

@dataclass
class Summary:
    """摘要指标"""
    name: str
    count: int = 0
    sum: float = 0.0
    quantiles: Dict[float, float] = field(default_factory=dict)
    labels: Dict[str, str] = field(default_factory=dict)
    help_text: Optional[str] = None
    _values: deque = field(default_factory=lambda: deque(maxlen=1000))
    
    def observe(self, value: float):
        """观察值"""
        self.count += 1
        self.sum += value
        self._values.append(value)
        self._update_quantiles()
    
    def _update_quantiles(self):
        """更新分位数"""
        if not self._values:
            return
        
        sorted_values = sorted(self._values)
        n = len(sorted_values)
        
        # 计算常用分位数
        quantiles_to_calc = [0.5, 0.9, 0.95, 0.99]
        for q in quantiles_to_calc:
            index = int(q * (n - 1))
            self.quantiles[q] = sorted_values[index]
    
    def to_metrics(self) -> List[Metric]:
        """转换为指标列表"""
        metrics = []
        
        # 分位数指标
        for quantile, value in self.quantiles.items():
            quantile_labels = self.labels.copy()
            quantile_labels['quantile'] = str(quantile)
            metrics.append(Metric(
                name=self.name,
                metric_type=MetricType.SUMMARY,
                value=value,
                labels=quantile_labels,
                timestamp=datetime.utcnow(),
                help_text=self.help_text
            ))
        
        # 总数指标
        metrics.append(Metric(
            name=f"{self.name}_count",
            metric_type=MetricType.SUMMARY,
            value=self.count,
            labels=self.labels,
            timestamp=datetime.utcnow(),
            help_text=self.help_text
        ))
        
        # 总和指标
        metrics.append(Metric(
            name=f"{self.name}_sum",
            metric_type=MetricType.SUMMARY,
            value=self.sum,
            labels=self.labels,
            timestamp=datetime.utcnow(),
            help_text=self.help_text
        ))
        
        return metrics

@runtime_checkable
class IMetricsCollector(Protocol):
    """指标收集器接口"""
    
    def collect_counter(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集计数器指标"""
        ...
    
    def collect_gauge(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集仪表盘指标"""
        ...
    
    def collect_histogram(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集直方图指标"""
        ...
    
    def collect_summary(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集摘要指标"""
        ...
    
    def get_metrics(self) -> List[Metric]:
        """获取所有指标"""
        ...
    
    def clear_metrics(self):
        """清空指标"""
        ...

class InMemoryCollector(IMetricsCollector):
    """内存指标收集器"""
    
    def __init__(self):
        self.counters: Dict[str, Counter] = {}
        self.gauges: Dict[str, Gauge] = {}
        self.histograms: Dict[str, Histogram] = {}
        self.summaries: Dict[str, Summary] = {}
        self._lock = threading.Lock()
        self.logger = get_logger("metrics_collector")
    
    def _get_key(self, name: str, labels: Dict[str, str] = None) -> str:
        """生成指标键"""
        if not labels:
            return name
        
        label_str = ",".join(f"{k}={v}" for k, v in sorted(labels.items()))
        return f"{name}{{{label_str}}}"
    
    def collect_counter(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集计数器指标"""
        key = self._get_key(name, labels)
        
        with self._lock:
            if key not in self.counters:
                self.counters[key] = Counter(
                    name=name,
                    labels=labels or {},
                    help_text=help_text
                )
            
            self.counters[key].inc(value)
    
    def collect_gauge(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集仪表盘指标"""
        key = self._get_key(name, labels)
        
        with self._lock:
            if key not in self.gauges:
                self.gauges[key] = Gauge(
                    name=name,
                    labels=labels or {},
                    help_text=help_text
                )
            
            self.gauges[key].set(value)
    
    def collect_histogram(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集直方图指标"""
        key = self._get_key(name, labels)
        
        with self._lock:
            if key not in self.histograms:
                self.histograms[key] = Histogram(
                    name=name,
                    labels=labels or {},
                    help_text=help_text
                )
            
            self.histograms[key].observe(value)
    
    def collect_summary(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集摘要指标"""
        key = self._get_key(name, labels)
        
        with self._lock:
            if key not in self.summaries:
                self.summaries[key] = Summary(
                    name=name,
                    labels=labels or {},
                    help_text=help_text
                )
            
            self.summaries[key].observe(value)
    
    def get_metrics(self) -> List[Metric]:
        """获取所有指标"""
        metrics = []
        
        with self._lock:
            # 计数器指标
            for counter in self.counters.values():
                metrics.append(counter.to_metric())
            
            # 仪表盘指标
            for gauge in self.gauges.values():
                metrics.append(gauge.to_metric())
            
            # 直方图指标
            for histogram in self.histograms.values():
                metrics.extend(histogram.to_metrics())
            
            # 摘要指标
            for summary in self.summaries.values():
                metrics.extend(summary.to_metrics())
        
        return metrics
    
    def clear_metrics(self):
        """清空指标"""
        with self._lock:
            self.counters.clear()
            self.gauges.clear()
            self.histograms.clear()
            self.summaries.clear()

class PrometheusCollector(IMetricsCollector):
    """Prometheus指标收集器"""
    
    def __init__(self):
        try:
            from prometheus_client import Counter as PrometheusCounter
            from prometheus_client import Gauge as PrometheusGauge
            from prometheus_client import Histogram as PrometheusHistogram
            from prometheus_client import Summary as PrometheusSummary
            from prometheus_client import REGISTRY, generate_latest
            
            self.PrometheusCounter = PrometheusCounter
            self.PrometheusGauge = PrometheusGauge
            self.PrometheusHistogram = PrometheusHistogram
            self.PrometheusSummary = PrometheusSummary
            self.REGISTRY = REGISTRY
            self.generate_latest = generate_latest
            
        except ImportError:
            raise ImportError("prometheus_client library is required for PrometheusCollector")
        
        self.counters: Dict[str, Any] = {}
        self.gauges: Dict[str, Any] = {}
        self.histograms: Dict[str, Any] = {}
        self.summaries: Dict[str, Any] = {}
        self._lock = threading.Lock()
        self.logger = get_logger("prometheus_collector")
    
    def _get_metric_name(self, name: str) -> str:
        """规范化指标名称"""
        # Prometheus指标名称只能包含字母、数字和下划线
        return name.replace(".", "_").replace("-", "_")
    
    def collect_counter(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集计数器指标"""
        metric_name = self._get_metric_name(name)
        
        with self._lock:
            if metric_name not in self.counters:
                label_names = list(labels.keys()) if labels else []
                self.counters[metric_name] = self.PrometheusCounter(
                    metric_name,
                    help_text or f"Counter metric {name}",
                    labelnames=label_names
                )
            
            counter = self.counters[metric_name]
            if labels:
                counter.labels(**labels).inc(value)
            else:
                counter.inc(value)
    
    def collect_gauge(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集仪表盘指标"""
        metric_name = self._get_metric_name(name)
        
        with self._lock:
            if metric_name not in self.gauges:
                label_names = list(labels.keys()) if labels else []
                self.gauges[metric_name] = self.PrometheusGauge(
                    metric_name,
                    help_text or f"Gauge metric {name}",
                    labelnames=label_names
                )
            
            gauge = self.gauges[metric_name]
            if labels:
                gauge.labels(**labels).set(value)
            else:
                gauge.set(value)
    
    def collect_histogram(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集直方图指标"""
        metric_name = self._get_metric_name(name)
        
        with self._lock:
            if metric_name not in self.histograms:
                label_names = list(labels.keys()) if labels else []
                self.histograms[metric_name] = self.PrometheusHistogram(
                    metric_name,
                    help_text or f"Histogram metric {name}",
                    labelnames=label_names
                )
            
            histogram = self.histograms[metric_name]
            if labels:
                histogram.labels(**labels).observe(value)
            else:
                histogram.observe(value)
    
    def collect_summary(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """收集摘要指标"""
        metric_name = self._get_metric_name(name)
        
        with self._lock:
            if metric_name not in self.summaries:
                label_names = list(labels.keys()) if labels else []
                self.summaries[metric_name] = self.PrometheusSummary(
                    metric_name,
                    help_text or f"Summary metric {name}",
                    labelnames=label_names
                )
            
            summary = self.summaries[metric_name]
            if labels:
                summary.labels(**labels).observe(value)
            else:
                summary.observe(value)
    
    def get_metrics(self) -> List[Metric]:
        """获取所有指标"""
        # Prometheus指标通过generate_latest()方法导出
        # 这里返回空列表，实际使用时应该通过/metrics端点访问
        return []
    
    def clear_metrics(self):
        """清空指标"""
        # Prometheus指标无法直接清空，需要重新创建
        with self._lock:
            self.counters.clear()
            self.gauges.clear()
            self.histograms.clear()
            self.summaries.clear()
    
    def export_metrics(self) -> bytes:
        """导出Prometheus格式指标"""
        return self.generate_latest(self.REGISTRY)

class MetricsManager:
    """指标管理器"""
    
    def __init__(self, collector: IMetricsCollector = None):
        self.collector = collector or InMemoryCollector()
        self.logger = get_logger("metrics_manager")
    
    def counter(self, name: str, value: float = 1.0, labels: Dict[str, str] = None, help_text: str = None):
        """记录计数器指标"""
        try:
            self.collector.collect_counter(name, value, labels, help_text)
        except Exception as e:
            self.logger.error(f"Failed to collect counter metric {name}: {e}")
    
    def gauge(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """记录仪表盘指标"""
        try:
            self.collector.collect_gauge(name, value, labels, help_text)
        except Exception as e:
            self.logger.error(f"Failed to collect gauge metric {name}: {e}")
    
    def histogram(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """记录直方图指标"""
        try:
            self.collector.collect_histogram(name, value, labels, help_text)
        except Exception as e:
            self.logger.error(f"Failed to collect histogram metric {name}: {e}")
    
    def summary(self, name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
        """记录摘要指标"""
        try:
            self.collector.collect_summary(name, value, labels, help_text)
        except Exception as e:
            self.logger.error(f"Failed to collect summary metric {name}: {e}")
    
    def get_metrics(self) -> List[Metric]:
        """获取所有指标"""
        return self.collector.get_metrics()
    
    def clear_metrics(self):
        """清空指标"""
        self.collector.clear_metrics()

# 全局指标管理器
_metrics_manager: Optional[MetricsManager] = None

def get_metrics_manager() -> MetricsManager:
    """获取全局指标管理器"""
    global _metrics_manager
    if _metrics_manager is None:
        _metrics_manager = MetricsManager()
    return _metrics_manager

def set_metrics_manager(manager: MetricsManager):
    """设置全局指标管理器"""
    global _metrics_manager
    _metrics_manager = manager

# 便捷函数
def collect_metrics(collector: IMetricsCollector):
    """设置指标收集器"""
    manager = get_metrics_manager()
    manager.collector = collector

def increment(name: str, value: float = 1.0, labels: Dict[str, str] = None, help_text: str = None):
    """增加计数器"""
    get_metrics_manager().counter(name, value, labels, help_text)

def set_gauge(name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
    """设置仪表盘值"""
    get_metrics_manager().gauge(name, value, labels, help_text)

def observe_histogram(name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
    """观察直方图值"""
    get_metrics_manager().histogram(name, value, labels, help_text)

def observe_summary(name: str, value: float, labels: Dict[str, str] = None, help_text: str = None):
    """观察摘要值"""
    get_metrics_manager().summary(name, value, labels, help_text)

# 装饰器
def timer(name: str = None, labels: Dict[str, str] = None, help_text: str = None):
    """计时装饰器"""
    def decorator(func: Union[F, AsyncF]) -> Union[F, AsyncF]:
        metric_name = name or f"{func.__module__}.{func.__name__}_duration"
        
        if asyncio.iscoroutinefunction(func):
            @functools.wraps(func)
            async def async_wrapper(*args, **kwargs):
                start_time = time.time()
                try:
                    result = await func(*args, **kwargs)
                    return result
                finally:
                    duration = time.time() - start_time
                    observe_histogram(metric_name, duration, labels, help_text)
            return async_wrapper
        else:
            @functools.wraps(func)
            def sync_wrapper(*args, **kwargs):
                start_time = time.time()
                try:
                    result = func(*args, **kwargs)
                    return result
                finally:
                    duration = time.time() - start_time
                    observe_histogram(metric_name, duration, labels, help_text)
            return sync_wrapper
    
    return decorator

def count_calls(name: str = None, labels: Dict[str, str] = None, help_text: str = None):
    """调用计数装饰器"""
    def decorator(func: Union[F, AsyncF]) -> Union[F, AsyncF]:
        metric_name = name or f"{func.__module__}.{func.__name__}_calls"
        
        if asyncio.iscoroutinefunction(func):
            @functools.wraps(func)
            async def async_wrapper(*args, **kwargs):
                increment(metric_name, 1.0, labels, help_text)
                return await func(*args, **kwargs)
            return async_wrapper
        else:
            @functools.wraps(func)
            def sync_wrapper(*args, **kwargs):
                increment(metric_name, 1.0, labels, help_text)
                return func(*args, **kwargs)
            return sync_wrapper
    
    return decorator

# 示例使用
if __name__ == "__main__":
    # 创建指标管理器
    manager = get_metrics_manager()
    
    # 记录指标
    manager.counter("requests_total", 1, {"method": "GET", "endpoint": "/api/users"})
    manager.gauge("active_connections", 42)
    manager.histogram("request_duration", 0.123, {"method": "GET"})
    manager.summary("response_size", 1024, {"endpoint": "/api/data"})
    
    # 使用装饰器
    @timer("database_query_duration")
    @count_calls("database_query_calls")
    async def query_database():
        await asyncio.sleep(0.1)  # 模拟数据库查询
        return {"result": "data"}
    
    # 使用Prometheus收集器
    try:
        prometheus_collector = PrometheusCollector()
        set_metrics_manager(MetricsManager(prometheus_collector))
        
        # 记录指标
        increment("app_requests_total", 1, {"method": "POST"})
        set_gauge("app_memory_usage", 128.5)
        
    except ImportError:
        print("Prometheus client not installed, using in-memory collector")
